package graph;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

/*
 * authors: Clayson Celes and Andrey Bicalho
 */
public class Graph {

	/** Stores all vertices of the graph */
	private Map<Point, Vertex> vertices;

	/** This structure represents the incidence matrix */
	private HashMap<Vertex, HashSet<Edge>> incidenceVertexMap = new LinkedHashMap<Vertex, HashSet<Edge>>();

	public Graph() {
		vertices = new HashMap<Point, Vertex>();
	}

	/*
	 * Add a new node to graph
	 * @param:
	 * node: new node
	 */
	public boolean addVertex(Vertex node) {
		vertices.put(node.getCoordinates(), node);
		if (incidenceVertexMap.containsKey(node)) {
			return false;
		}

		HashSet<Edge> edges = new LinkedHashSet<Edge>();
		incidenceVertexMap.put(node, edges);
		return true;
	}

	/*
	 * Add various nodes to graph
	 * @param:
	 * node: array of nodes 
	 */
	public void addVertices(Vertex... nodes) {
		for (Vertex vertex : nodes) {
			addVertex(vertex);
		}
	}

	public boolean removeVertex(Vertex vertex) {
		if (!incidenceVertexMap.containsKey(vertex)) {
			return false;
		} else {
			incidenceVertexMap.remove(vertex);
			return true;
		}
	}

	public Edge addEdge(Edge newEdge) {
		
		HashSet<Edge> edgeSet1 = incidenceVertexMap.get(newEdge.getNode1());
		if (edgeSet1 == null) {
			return null;
		}
		if (!edgeSet1.contains(newEdge)) {
			edgeSet1.add(newEdge);
		}

		HashSet<Edge> edgeSet2 = incidenceVertexMap.get(newEdge.getNode2());
		if (edgeSet2 == null) {
			return null;
		}
		if (!edgeSet2.contains(newEdge)) {
			edgeSet2.add(newEdge);
		}

		return newEdge;
	}

	public HashSet<Edge> addEdges(Edge... edges) {
		HashSet<Edge> addedEdges = new LinkedHashSet<Edge>();
		for (Edge edge : edges) {
			addedEdges.add(addEdge(edge));
		}
		return addedEdges;
	}

	public boolean removeEdge(Edge edge) {

		HashSet<Edge> edgeSet = incidenceVertexMap.get(edge.getNode1());
		if (edgeSet == null) {
			return false;
		} else {
			if (!edgeSet.remove(edge)) {
				return false;
			}
		}

		HashSet<Edge> edgeSet2 = incidenceVertexMap.get(edge.getNode2());
		if (edgeSet2 == null) {
			return false;
		} else {
			if (!edgeSet2.remove(edge)) {
				return false;
			}
		}

		return true;
	}

	public Set<Vertex> getAllVertices() {
		return incidenceVertexMap.keySet();
	}

	public Set<Edge> getAllEdges() {
		Collection<HashSet<Edge>> hashSets = incidenceVertexMap.values();
		Set<Edge> allEdges = new HashSet<Edge>();
		for (HashSet<Edge> set : hashSets) {
			allEdges.addAll(set);
		}
		return allEdges;
	}

	public HashSet<Edge> getEdgesByVertex(Vertex vertex) {
		return incidenceVertexMap.get(vertex);
	}

	public HashSet<Vertex> getNeighborsByVertex(Vertex vertex) {
		HashSet<Vertex> neighbors = new LinkedHashSet<Vertex>();
		HashSet<Edge> edges = getEdgesByVertex(vertex);
		for (Edge edge : edges) {
			if (!edge.getNode1().equals(vertex)) {
				neighbors.add(edge.getNode1());
			} else {
				neighbors.add(edge.getNode2());
			}
		}
		return neighbors;
	}

	public Vertex getVertex(int row, int col) {
		return vertices.get(new Point(col, row));
	}
	
	public Map<Point, Vertex> getVertices(){
		return vertices;
	}
	
	public HashMap<Vertex, HashSet<Edge>> getEdges(){
		return incidenceVertexMap;
	}

}
